﻿using DTO;
using Microsoft.AspNetCore.Http;
using Model;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Net.WebSockets;
using System.Text;
using Util;
using SqlSugar;

namespace API.Middlewares
{
    public class HeartbeatWebSocketMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TimeSpan _heartbeatInterval;
        private List<WebSocket> _clients;
        private readonly IWebSocketManager _webSocketManager;
        public static ISqlSugarClient _db;
        private readonly IServiceScopeFactory _scopeFactory;

        tFace faceModel = new tFace();

        public HeartbeatWebSocketMiddleware(RequestDelegate next, TimeSpan heartbeatInterval, IWebSocketManager webSocketManager, IServiceScopeFactory scopeFactory)
        {
            _next = next;
            _heartbeatInterval = heartbeatInterval;
            _clients = new List<WebSocket>();
            _webSocketManager = webSocketManager;
            _scopeFactory = scopeFactory;
        }

        private void OnClientConnected(WebSocket webSocket)
        {
            _webSocketManager.AddClientAsync(webSocket);
        }

        public async Task InvokeAsync(HttpContext context)
        {
            using (var scope = _scopeFactory.CreateScope())
            {
                _db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
                // 使用 sqlSugar 操作数据库
            }
            if (context.WebSockets.IsWebSocketRequest)
            {
                string deviceId = context.Request.Query["deviceId"].FirstOrDefault();

                if (string.IsNullOrEmpty(deviceId))
                {
                    // 用户ID缺失，返回错误响应
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("Missing deviceId");
                    return;
                }
                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                await _webSocketManager.AddClientAsync(webSocket);
                await HandleWebSocket(context, webSocket);
                await _webSocketManager.RemoveClientAsync(webSocket);
            }
            else
            {
                await _next.Invoke(context);
            }
        }

        private async Task HandleWebSocket(HttpContext context, WebSocket webSocket)
        {
            var buffer = new byte[1024 * 4];
            CancellationTokenSource cts = new CancellationTokenSource();
            Task heartbeatTask = SendHeartbeats(webSocket, cts.Token);

            try
            {
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);



                while (!result.CloseStatus.HasValue)
                {
                    // 处理接收到的消息  
                    string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                    // TODO: 根据你的需求处理消息 
                    //下发人员信息 
                    if (!message.Contains("ping"))
                    {
                        Util.NLOG.LogHelper.Info(message);
                    }
                    string deviceId = context.Request.Query["deviceId"].FirstOrDefault();
                    //Util.NLOG.LogHelper.Info("deviceId:"+ deviceId);
                    switch (deviceId)
                    {
                        case "H70982"://食堂
                            if (message != null)
                            {
                                sendFaceDeviceOutput dao = new sendFaceDeviceOutput();
                                JObject jo = JObject.Parse(message);
                                if (jo != null)
                                {
                                    string cmd = jo["cmd"].ToString();
                                    if (cmd != null)
                                    {
                                        switch (cmd)
                                        {
                                            case "ping":
                                                HeartbeatOutput dao1 = new HeartbeatOutput();
                                                dao1.cmd = "pong";
                                                dao1.sn = "H70982";
                                                DateTimeOffset now = DateTimeOffset.Now;
                                                var timestamp = now.ToUnixTimeSeconds().ToString();
                                                dao1.timestamp = timestamp;
                                                await sendMessage(JsonConvert.SerializeObject(dao1));
                                                break;
                                            case "enter":
                                                string user_id = string.Empty;
                                                string user_name = string.Empty;
                                                var list = jo["data"];
                                                foreach (var item in list)
                                                {
                                                    dao = new sendFaceDeviceOutput();

                                                    dao.cmd = "to_device";
                                                    dao.from = "123";
                                                    dao.to = "H70982";
                                                    dao.extra = "extra";
                                                    dao.data = new userData
                                                    {
                                                        cmd = "addUser",
                                                        user_id = item["user_id"].Value<string>(),
                                                        name = item["user_name"].Value<string>(),
                                                        tts_name = "",
                                                        face_template = item["photo"].Value<string>(),
                                                        effect_time = "",
                                                        id_valid = "",
                                                        Ic = "",
                                                        confidence_level = 80,
                                                        phone = "",
                                                        mode = "0",
                                                    };
                                                    await sendMessage(JsonConvert.SerializeObject(dao));
                                                }
                                                break;
                                            case "to_client":
                                                FaceReturn data = JsonConvert.DeserializeObject<FaceReturn>(jo["data"].ToString());
                                                if (data != null)
                                                {
                                                    var faceModel = _db.Queryable<tFace>().Where(x => x.FUserId == data.user_id).First();
                                                    if (data.code == 0)
                                                    {
                                                        faceModel.FEnter = true;
                                                        faceModel.FErrMsg = "";
                                                        _db.Updateable(faceModel).ExecuteCommand();

                                                        FaceReturn face = new FaceReturn();
                                                        face.code = 0;
                                                        face.cmd = "addUserRet";
                                                        face.msg = "";
                                                        await sendMessage(JsonConvert.SerializeObject(face));
                                                    }
                                                    else
                                                    {
                                                        faceModel.FErrMsg = data.msg;
                                                        _db.Updateable(faceModel).ExecuteCommand();
                                                        await sendMessage(JsonConvert.SerializeObject(data));
                                                    }
                                                }
                                                break;
                                            case "setDoor":
                                                AddUserDao obj = new AddUserDao();


                                                obj.cmd = "to_device";
                                                obj.extra = "eeee";
                                                obj.from = "123";
                                                obj.to = "H70982";
                                                obj.data = new setDoor
                                                {
                                                    cmd = "setDoor",
                                                    value = "on",
                                                    user_id = string.Empty,
                                                    type = string.Empty
                                                };
                                                var str = JsonConvert.SerializeObject(obj);
                                                await webSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj))), WebSocketMessageType.Text, true, cts.Token);
                                                break;
                                            case "setStrangerRecognitionV2":
                                                AddUserDao obj1 = new AddUserDao();


                                                obj1.cmd = "to_device";
                                                obj1.extra = "eeee";
                                                obj1.from = "123";
                                                obj1.to = "H70982";
                                                obj1.data = new setStrangerRecognitionV2
                                                {
                                                    cmd = "setStrangerRecognitionV2",
                                                    value = "0",
                                                };
                                                for (int i = 0; i < _clients.Count; i++)
                                                {
                                                    if (_clients[i] != webSocket && _clients[i].State == WebSocketState.Open)
                                                    {
                                                        await _clients[i].SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj1))), WebSocketMessageType.Text, true, cts.Token);
                                                    }
                                                }
                                                break;
                                            default:
                                                break;

                                        }
                                    }
                                }

                            }
                            break;
                        case "QT75031"://门禁
                            if (message != null)
                            {
                                sendFaceDeviceOutput dao = new sendFaceDeviceOutput();
                                JObject jo = JObject.Parse(message);
                                if (jo != null)
                                {
                                    string cmd = jo["cmd"].ToString();
                                    if (cmd != null)
                                    {
                                        dao = new sendFaceDeviceOutput();

                                        dao.cmd = "to_device";
                                        dao.from = "123";
                                        dao.to = "QT75031";
                                        dao.extra = "";
                                        switch (cmd)
                                        {
                                            case "ping":
                                                HeartbeatOutput dao1 = new HeartbeatOutput();
                                                dao1.cmd = "pong";
                                                dao1.sn = "QT75031";
                                                DateTimeOffset now = DateTimeOffset.Now;
                                                var timestamp = now.ToUnixTimeSeconds().ToString();
                                                dao1.timestamp = timestamp;
                                                await sendMessage(JsonConvert.SerializeObject(dao1));

                                                break;
                                            case "enter"://下发人员到门禁设备
                                                string user_id = string.Empty;
                                                string user_name = string.Empty;
                                                var list = jo["data"];
                                                List<DTO.valid_cycle> cyclesList = new List<DTO.valid_cycle>();
                                                DTO.valid_cycle cycle = new DTO.valid_cycle();
                                                tPassRule rulePModel = new tPassRule();
                                                string pass_rule_id = "";


                                                foreach (var item in list)
                                                {
                                                    cyclesList = new List<DTO.valid_cycle>();
                                                    foreach (var token in item["valid_cycle"].Children())
                                                    {
                                                        cycle = new DTO.valid_cycle();
                                                        cycle.start_time = token["start_time"].ToString();
                                                        cycle.end_time = token["end_time"].ToString();
                                                        cyclesList.Add(cycle);
                                                        // 处理每一个token
                                                    }
                                                    //var jsonArray = JArray.Parse(item["valid_cycle"].Value<string>());
                                                    //通过用户ID获取组ID,再通过组ID获取通行规则ID
                                                    string uid = item["user_id"].ToString();
                                                    pass_rule_id = _db.Queryable<tRuleStaff>().Where(c => c.FUserId == uid).First()?.FRuleId.ToString() ?? "";
                                                    dao.data = new userData
                                                    {
                                                        cmd = "addUser",
                                                        user_id = item["user_id"].Value<string>(),
                                                        name = item["user_name"].Value<string>(),
                                                        pass_rule_id = pass_rule_id,
                                                        tts_name = "",
                                                        face_template = item["photo"].Value<string>(),
                                                        effect_time = "",
                                                        id_valid = "",
                                                        Ic = "",
                                                        confidence_level = 80,
                                                        phone = "",
                                                        mode = "0",
                                                    };
                                                    await sendMessage(JsonConvert.SerializeObject(dao));
                                                }
                                                break;
                                            case "enterAll":
                                                var faceList = _db.Queryable<tFace>().Where(x => !x.FDeleted).ToList();

                                                if (faceList != null && faceList.Count > 0)
                                                {
                                                    cyclesList = new List<DTO.valid_cycle>();
                                                    cycle = new DTO.valid_cycle();
                                                    foreach (var item in faceList)
                                                    {
                                                        cyclesList = JsonConvert.DeserializeObject<List<DTO.valid_cycle>>(item.FValidCycle);

                                                        dao.data = new userData
                                                        {
                                                            cmd = "addUser",
                                                            user_id = item.FUserId,
                                                            name = item.FName,
                                                            tts_name = "",
                                                            face_template = item.FPhoto,
                                                            effect_time = "",
                                                            id_valid = "",
                                                            Ic = "",
                                                            confidence_level = 80,
                                                            phone = "",
                                                            mode = "0",
                                                            valid_cycle = cyclesList,
                                                        };
                                                        await sendMessage(JsonConvert.SerializeObject(dao));
                                                    }
                                                }
                                                break;
                                            case "to_client"://设备端返回
                                                try
                                                {

                                                    FaceReturn data = JsonConvert.DeserializeObject<FaceReturn>(jo["data"].ToString());

                                                    if (data != null)
                                                    {
                                                        switch (data.cmd)
                                                        {
                                                            case "addUserRet":
                                                                faceModel = _db.Queryable<tFace>().Where(x => x.FUserId == data.user_id && !x.FDeleted).First();
                                                                if (data.code == 0)
                                                                {

                                                                    faceModel.FDoorEnter = true;
                                                                    faceModel.FDoorErrMsg = data.msg;
                                                                }
                                                                else
                                                                {
                                                                    faceModel.FDoorErrMsg = data.msg;
                                                                    faceModel.FDoorEnter = false;
                                                                }

                                                                _db.Updateable(faceModel).ExecuteCommand();
                                                                FaceReturn face = new FaceReturn();
                                                                face.code = 0;
                                                                face.cmd = "addUserRet";
                                                                face.msg = "";
                                                                await sendMessage(JsonConvert.SerializeObject(face));

                                                                break;
                                                            case "delUserRet":
                                                                faceModel = _db.Queryable<tFace>().Where(x => x.FUserId == data.user_id).First();
                                                                faceModel.FDeleted = true;
                                                                _db.Updateable(faceModel).ExecuteCommand();
                                                                face = new FaceReturn();
                                                                face.code = 0;
                                                                face.cmd = "delUserRet";
                                                                face.msg = "";
                                                                await sendMessage(JsonConvert.SerializeObject(face));

                                                                break;
                                                            case "delPassRuleRet":
                                                                delPassRuleData delRuledata = new delPassRuleData();
                                                                delRuledata.cmd = "delPassRuleRet";
                                                                await sendMessage(JsonConvert.SerializeObject(delRuledata));

                                                                break;
                                                            case "addPassRuleRet"://下发通行规则返回

                                                                tPassRule passModel = new tPassRule();
                                                                foreach (var item in data.rule_id)
                                                                {
                                                                    passModel = _db.Queryable<tPassRule>().Where(c => c.Id == item.ToString()).First();
                                                                    passModel.FEnter = true;
                                                                    _db.Updateable(passModel).ExecuteCommand();
                                                                }

                                                                addPassRuleRetData addPassRule = new addPassRuleRetData();
                                                                addPassRule.cmd = "addPassRuleRet";
                                                                addPassRule.rule_id = data.rule_id[0];
                                                                await sendMessage(JsonConvert.SerializeObject(addPassRule));

                                                                break;
                                                            case "setVisitorApplyValueRet"://下发二维码返回
                                                                int a = 1;

                                                                if (data.code == 0)
                                                                {
                                                                    //var model = _tVisitor.GetById(Convert.ToInt32(jo["extra"]));
                                                                    //model.FStatus = 1;
                                                                    //model.UpdateTime = DateTime.Now;
                                                                    //_tVisitor.Update(model);
                                                                }
                                                                setVisitorApplyValueRetData setVisitorApplyValueRet = new setVisitorApplyValueRetData();
                                                                setVisitorApplyValueRet.cmd = "setVisitorApplyValueRet";
                                                                setVisitorApplyValueRet.code = data.code;
                                                                setVisitorApplyValueRet.msg = data.msg;
                                                                await sendMessage(JsonConvert.SerializeObject(setVisitorApplyValueRet));

                                                                break;
                                                            case "getPassRuleByUserIdRet"://获取用户规则返回
                                                                getPassRuleByUserIdRetData getPassRuleByUserIdRet = new getPassRuleByUserIdRetData();
                                                                getPassRuleByUserIdRet.cmd = "getPassRuleByUserIdRet";
                                                                await sendMessage(JsonConvert.SerializeObject(getPassRuleByUserIdRet));

                                                                break;
                                                            case "getUserDetailsRet"://获取人脸信息返回
                                                                getUserDetailsRet getUserDetailsRetData = new getUserDetailsRet();
                                                                getUserDetailsRetData = JsonConvert.DeserializeObject<getUserDetailsRet>(jo["data"].ToString());
                                                                await sendMessage(JsonConvert.SerializeObject(getUserDetailsRetData));
                                                                break;
                                                        }
                                                    }
                                                }
                                                catch (Exception ex)
                                                {

                                                    throw;
                                                }
                                                break;
                                            case "delUser":
                                                dao.data = new delUserData
                                                {
                                                    cmd = "delUser",
                                                    user_id = jo["data"]["user_id"].Value<string>(),
                                                };
                                                await sendMessage(JsonConvert.SerializeObject(dao));

                                                break;

                                            case "setVisitorApplyValue"://下发二维码
                                                dao.extra = jo["extra"].ToString();
                                                dao.data = new setVisitorApplyData
                                                {
                                                    cmd = "setVisitorApplyValue",
                                                    photo = jo["photo"].Value<string>(),
                                                    url = jo["url"].Value<string>()
                                                };

                                                await sendMessage(JsonConvert.SerializeObject(dao));
                                                break;

                                            case "addPassRule"://下发通行规则
                                                var ruleModel = _db.Queryable<tPassRule>().Where(x => x.Id == jo["id"].ToString()).First();
                                                List<RuleData> rules = new List<RuleData>();
                                                RuleData ruleData = new RuleData();
                                                ruleData.weekday = JsonConvert.DeserializeObject<List<Weekday>>(ruleModel.FWeekday);
                                                ruleData.impassable = JsonConvert.DeserializeObject<List<ImpassableInfo>>(ruleModel.FImpassable);
                                                ruleData.passables = JsonConvert.DeserializeObject<List<StartAndEnd>>(ruleModel.FPassable);
                                                ruleData.id = ruleModel.Id.ToString();
                                                ruleData.rule_name = ruleModel.FRuleName;
                                                ruleData.version = Convert.ToInt32(ruleModel.FVersion);
                                                rules.Add(ruleData);
                                                dao.data = new addPassRuleData
                                                {
                                                    cmd = "addPassRule",
                                                    rule = rules
                                                };

                                                Util.NLOG.LogHelper.Info(JsonConvert.SerializeObject(dao));
                                                await sendMessage(JsonConvert.SerializeObject(dao));

                                                break;
                                            case "delPassRule"://删除通行规则
                                                List<int> rule_ids = new List<int>();

                                                foreach (var token in jo["rule_ids"].Children())
                                                {

                                                    rule_ids.Add(Convert.ToInt32(token));
                                                    // 处理每一个token
                                                }
                                                dao.data = new delPassRuleData
                                                {
                                                    cmd = "delPassRule",
                                                    is_delete_all = jo["is_delete_all"].Value<int>(),
                                                    rule_ids = rule_ids
                                                };
                                                await sendMessage(JsonConvert.SerializeObject(dao));

                                                break;

                                            case "getPassRuleByUserId"://获取用户通行规则

                                                dao.data = new getPassRuleByUserIdData
                                                {
                                                    cmd = "getPassRuleByUserId",
                                                    user_id = jo["user_id"].Value<string>(),
                                                };

                                                await sendMessage(JsonConvert.SerializeObject(dao));
                                                break;
                                            case "setPassRuleByRule"://下发通行规则到
                                                var ids = _db.Queryable<tRuleStaff>().Where(x => x.FRuleId == jo["ruleId"].ToString()).Select(x => x.FUserId).ToList();
                                                var userList = _db.Queryable<tStaff>().Where(a => ids.Contains(a.FUserId)).Select(t => new userOutput
                                                {
                                                    userId = t.FUserId.ToString(),
                                                    userName = t.FUserName.ToString(),
                                                }).ToList();

                                                foreach (var item in userList)
                                                {
                                                    //var cyclesList = JsonConvert.DeserializeObject<List<valid_cycle>>(item["valid_cycle"].Value<string>());
                                                    //通过用户ID获取组ID,再通过组ID获取通行规则ID

                                                    dao.data = new userData
                                                    {
                                                        cmd = "addUser",
                                                        user_id = item.userId,
                                                        name = item.userName,
                                                        pass_rule_id = jo["ruleId"].Value<string>(),
                                                        tts_name = "",
                                                        face_template = _db.Queryable<tFace>().Where(c => c.FUserId == item.userId && !c.FDeleted).First()?.FPhoto,
                                                        effect_time = "",
                                                        id_valid = "",
                                                        Ic = "",
                                                        confidence_level = 80,
                                                        phone = "",
                                                        mode = "0",

                                                    };
                                                    await sendMessage(JsonConvert.SerializeObject(dao));
                                                }
                                                break;
                                            case "getUserDetails"://获取人脸信息
                                                dao.data = new getUserDetailsData
                                                {
                                                    cmd = "getUserDetails",
                                                    value = 0,
                                                    user_id = jo["user_id"].Value<string>(),
                                                    is_response_photo = false
                                                };
                                                await sendMessage(JsonConvert.SerializeObject(dao));
                                                break;

                                            default:
                                                break;

                                        }
                                    }
                                }

                            }

                            break;
                    }
                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);
                }
                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, cts.Token);
            }
            catch (OperationCanceledException ex)
            {
                // 连接关闭或取消操作，清理资源并退出
                //Console.WriteLine("Connection closed or operation canceled.");
                Util.NLOG.LogHelper.Error("Connection closed or operation canceled:" + ex.Message);
                // 可以在这里执行清理操作，例如关闭WebSocket
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing connection.", cts.Token);
            }
            finally
            {
                // 停止发送心跳包
                cts.Cancel();
                // 等待心跳任务完成
                await heartbeatTask;
                // 清理其他资源
                cts.Dispose();
                // 如果还有其他需要清理的资源，这里可以继续添加
            }
        }
        private async Task sendMessage(string message)
        {

            CancellationTokenSource cts = new CancellationTokenSource();
            foreach (var client in _webSocketManager.GetAllOpenClients()) // 或者使用_GetActiveClients()取决于你的选择
            {
                if (client.State == WebSocketState.Open)
                {
                    await client.SendAsync(
                        new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),
                        WebSocketMessageType.Text,
                        true,
                        cts.Token
                    );
                }
            }
        }

        private async Task SendHeartbeats(WebSocket webSocket, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(_heartbeatInterval, cancellationToken); // 等待指定的心跳间隔  
                try
                {
                }
                catch (Exception ex)
                {
                    // 连接已关闭，退出发送心跳包任务。  
                    break;
                }
            }
        }
    }
}
